﻿
#include <iostream>
#include <fstream>
#include <string>

#include <iostream>
#include <fstream>
#include <vector>

// UTF8 编码格式（xxx 是用来填充二进制 Unicode 码点的）
//
// 1字节	0xxxxxxx
// 2字节	110xxxxx_10xxxxxx
// 3字节	1110xxxx_10xxxxxx_10xxxxxx
// 4字节	11110xxx_10xxxxxx_10xxxxxx_10xxxxxx
// 5字节	111110xx_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx
// 6字节	1111110x_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx_10xxxxxx
//
// 有效的 Unicode 码点范围为 0-0x10FFFF，最多用到 4 字节 UTF8 编码

// using namespace std;

// 读取 UTF8 文件
std::vector<uint32_t> read_utf8_file(std::string filename)
{
	// 打开文件
	std::ifstream input_file(filename);
	if (!input_file.is_open())
	{
		perror("std::ifstream -> open()");
		exit(1);
	}

	// 跳过 UTF8 BOM（0xEFBBBF）
	if (input_file.get() != 0xEF || input_file.get() != 0xBB || input_file.get() != 0xBF)
	{
		input_file.seekg(0, std::ios::beg);
	}

	unsigned char utf8_chat;		   // UTF8 码点，涉及位运算，必须使用无符号数
	uint32_t utf8_wchat;			   // Unicode 码点
	std::vector<uint32_t> utf8_vector; // 用于存储转换结果的 Unicode 码点序列

	int utf8_len; // 单个 UTF8 字符的编码长度

	while ((utf8_chat = input_file.get()) && !input_file.eof())
	{
		if (utf8_chat < 0b10000000)
		{
			// 单字节编码
			utf8_wchat = utf8_chat;
		}
		else
		{
			// 多字节编码，获取编码长度
			if (utf8_chat > 0b11110100)
			{
				std::cout << (uint32_t)utf8_chat << std::endl;
				// 超出可用 Unicode 范围 0x10FFFF
				// 11110100_10001111_10111111_10111111
				fprintf(stderr, "Invalid unicode range\n");
				exit(1);
			}
			else if (utf8_chat >= 0b11110000)
			{
				utf8_len = 4;
			}
			else if (utf8_chat >= 0b11100000)
			{
				utf8_len = 3;
			}
			else if (utf8_chat >= 0b11000000)
			{
				utf8_len = 2;
			}
			else
			{
				// 首字节不能小于 0b11000000
				fprintf(stderr, "Invalid utf8 leading code");
				exit(1);
			}
			// 通过左移再右移的方法去掉首字节中的 UTF8 标记
			utf8_chat = utf8_chat << (utf8_len + 1);
			utf8_wchat = utf8_chat >> (utf8_len + 1);

			// 处理后续 UTF8 编码
			while (utf8_len > 1)
			{
				utf8_chat = input_file.get();
				// 如果 input_file 到达 eof，则 utf8_chat 会返回 255，刚好匹配下面的错误检查
				// 后续编码必须是 0b10xxxxxx 格式
				if (utf8_chat >= 0b11000000)
				{
					fprintf(stderr, "Invalid utf8 tailing code");
					exit(1);
				}
				utf8_len--;
				utf8_chat = utf8_chat & 0b00111111; // 去掉 UTF8 标记
				utf8_wchat = utf8_wchat << 6;		// 腾出 6 个 bit 的位置
				utf8_wchat += utf8_chat;			// 将去掉了 UTF8 标记的编码合并进来
			}
		}
		utf8_vector.push_back(utf8_wchat); // 存储解解析结果
	}
	return utf8_vector;
}

// 判断字符是否为可见字符（这里以 ASCII 为例，仅包括空格到~）
bool is_visible_char(uint32_t code_point)
{
	// 检查字符是否在ASCII的可见范围内（32-126）
	// 包括空格（32）、回车（10）和制表符（9）
	// • 8226
	// ’8217
	// “ 8220
	// ” 8221

	// ASCII 可见字符（空格到~）
	if (code_point >= 0x20 && code_point <= 0x7E)
	{
		return true;
	}
	// 控制字符（回车、制表符等）
	if (code_point == 0x09 || code_point == 0x0A || code_point == 0x0D)
	{
		return true;
	}
	// 常见标点符号和符号
	if (code_point >= 0x2000 && code_point <= 0x206F)
	{ // General Punctuation
		return true;
	}
	if (code_point >= 0x3000 && code_point <= 0x303F)
	{ // CJK Symbols and Punctuation
		return true;
	}
	if (code_point >= 0xFF00 && code_point <= 0xFFEF)
	{ // Halfwidth and Fullwidth Forms
		return true;
	}

	return false;
}

// 将 Unicode 码点转换为 UTF-8 编码的字节序列
void write_utf8_code_point(std::ostream &output, uint32_t code_point)
{
	if (code_point <= 0x7F)
	{
		output.put(static_cast<char>(code_point));
	}
	else if (code_point <= 0x7FF)
	{
		output.put(static_cast<char>(0xC0 | (code_point >> 6)));
		output.put(static_cast<char>(0x80 | (code_point & 0x3F)));
	}
	else if (code_point <= 0xFFFF)
	{
		output.put(static_cast<char>(0xE0 | (code_point >> 12)));
		output.put(static_cast<char>(0x80 | ((code_point >> 6) & 0x3F)));
		output.put(static_cast<char>(0x80 | (code_point & 0x3F)));
	}
	else if (code_point <= 0x10FFFF)
	{
		output.put(static_cast<char>(0xF0 | (code_point >> 18)));
		output.put(static_cast<char>(0x80 | ((code_point >> 12) & 0x3F)));
		output.put(static_cast<char>(0x80 | ((code_point >> 6) & 0x3F)));
		output.put(static_cast<char>(0x80 | (code_point & 0x3F)));
	}
	else
	{
		// 超出 Unicode 范围，这里可以选择忽略或者抛出异常
		fprintf(stderr, "Invalid Unicode code point\n");
	}
}

int main()
{
	std::string input_file("C:/Users/75154/Documents/zephyr 1/txt/input.txt");
	std::string output_file("C:/Users/75154/Documents/zephyr 1/txt/output.txt");

	std::vector<uint32_t> code_points = read_utf8_file(input_file);

	std::ofstream output(output_file);
	if (!output.is_open())
	{
		perror("std::ofstream -> open()");
		exit(1);
	}

	for (uint32_t code_point : code_points)
	{
		if (is_visible_char(code_point))
		{
			write_utf8_code_point(output, code_point); // 正确写入可见字符
		}
	}

	output.close();

	std::cout << "处理完成。" << std::endl;
	return 0;
}
